﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters;
using System.Runtime.Serialization.Formatters.Binary;
using System.Diagnostics;
using DiscreteEventModel;
using DiscreteEventModel.Common;


namespace TestConsole {

    using SystemMarshal = System.Runtime.InteropServices.Marshal;


    public struct MyStruct {
        public int @int;
        public double @double;
        private int _index;

        public MyStruct(int i, double d) {
            this.@int = i;
            this.@double = d;
            this._index = 0;
        }


    }


    public class MyClass {
        public int @int;
        public double @double;

        public MyClass() { }

        public MyClass(int i, double d) {
            this.@int = i;
            this.@double = d;
        }


    }

    class Program {

        public delegate void Test();

        static Test test = test5;
        static Random rand = new Random();

        static void Main(string[] args) {
            if(test != null) {
                test();
            }
        }

        private static void test1() {

            long offset = 0x10000000;
            long length = 0x20000000;

            using(MemoryMappedFile mmf = MemoryMappedFile.CreateFromFile(@"C:\Documents and Settings\Haymann\My Documents\Temp\mmf_test.dat", FileMode.OpenOrCreate, "mmf_test")) {

                char[] charArray = "Hello world!".ToCharArray();
                MemoryMappedViewAccessor accessor = mmf.CreateViewAccessor();
                accessor.WriteArray<char>(0, charArray, 0, charArray.Length);

                MemoryMappedViewAccessor accessor2 = mmf.CreateViewAccessor();
                char[] charArray2 = new char[charArray.Length - 1];
                accessor2.ReadArray<char>(0, charArray2, 0, charArray.Length);
            }

        }

        /*
                private static void test2() {

                    MemoryMappedFileWrapper mmfw = new MemoryMappedFileWrapper(@"C:\Documents and Settings\Haymann\My Documents\Temp\mmf_test.dat", "mmf_test", 16);

                    Random rand = new Random();

                    for(int i = 0; i <= 10000; i++) {
                        long offset;
                        mmfw.Write<char>("Hello world! This is Haymann! This is a testing of the MemoryMappedFileWrapper!" + rand.Next(), -1, out offset);

                        List<char> chars = new List<char>(mmfw.Read<char>(offset));
                        Console.WriteLine(new string(chars.ToArray()));
                        mmfw[offset] = null;
                    }

                }*/

        private static List<MyClass> getTestCollection(int count) {
            List<MyClass> list = new List<MyClass>();
            for(int j = 0; j < count; j++) {
                list.Add(new MyClass(rand.Next(), rand.NextDouble()));
            }
            return list;
        }

        /*
        private static void test3() {
            MemoryMappedFileWrapper mmfw = new MemoryMappedFileWrapper(@"C:\Documents and Settings\Haymann\My Documents\Temp\mmf_test.dat", "mmf_test", 2 * 1024);

            Dictionary<MemoryMappedFileWrapper.StreamHandle, List<MyClass>> collectionsByToken = new Dictionary<MemoryMappedFileWrapper.StreamHandle, List<MyClass>>();
            int collectionSize = 1024;
            BinaryFormatter formatter = new BinaryFormatter();
            for(int i = 0; i <= 100000; i++) {
                if(rand.Next() % 2 == 0) {
                    List<MyClass> originalCollection = getTestCollection(collectionSize);
                    using(MemoryMappedFileWrapper.WriteStream writeStream = mmfw.CreateWriteStream()) {
                        foreach(MyClass obj in originalCollection) {
                            //formatter.Serialize(writeStream, obj);
                            writeStream.Write(obj);
                        }
                        writeStream.Flush();
                        collectionsByToken.Add(writeStream.Handle, originalCollection);
                    }
                    Console.WriteLine(string.Format("Collection created, Collection remained: {0}", collectionsByToken.Count));
                } else if(collectionsByToken.Count > 0) {
                    MemoryMappedFileWrapper.StreamHandle token = collectionsByToken.Keys.First();
                    using(MemoryMappedFileWrapper.ReadStream readStream = mmfw.CreateReadStream(token)) {
                        foreach(MyClass origObj in collectionsByToken[token]) {
                            //MyStruct retrievedObj = (MyStruct)formatter.Deserialize(readStream);
                            MyClass retrievedObj = new MyClass();
                            readStream.Read(retrievedObj);
                            if(retrievedObj.@double != origObj.@double || retrievedObj.@int != origObj.@int) {
                                throw new Exception();
                            }
                        }
                    }
                    mmfw.DeallocateStream(token);
                    collectionsByToken.Remove(token);

                    Console.WriteLine(string.Format("Collection verified, Collection remained: {0}", collectionsByToken.Count));
                }


                //Console.WriteLine(string.Format("Count: {0}, AllocatedSize: {1}", i, mmfw.GetHeader().AllocatedSize));
            }
        }

        
        private static void test3() {
            MemoryMappedFileWrapper mmfw = new MemoryMappedFileWrapper(@"C:\Documents and Settings\Haymann\My Documents\Temp\mmf_test.dat", "mmf_test", 8*1024);

            Dictionary<long, List<MyStruct>> clusterOffsets = new Dictionary<long, List<MyStruct>>();
            int collectionSize = 128*1024;
            for(int i = 0; i <= 100000; i++) {
                if(rand.Next() % 2 == 0) {
                    long offset;
                    List<MyStruct> originalCollection = getTestCollection(collectionSize);
                    mmfw.Write<MyStruct>(originalCollection, -1, out offset);
                    clusterOffsets.Add(offset, originalCollection);
                } else if(clusterOffsets.Count > 0) {
                    long offset = clusterOffsets.Keys.First();
                    List<MyStruct> retrievedCollection = new List<MyStruct>(mmfw.Read<MyStruct>(offset, collectionSize));
                    List<MyStruct> originalCollection = clusterOffsets[offset];
                    for(int j = 0; j < collectionSize; j++) {
                        if(retrievedCollection[j].@double != originalCollection[j].@double || retrievedCollection[j].@int != originalCollection[j].@int) {
                            throw new Exception();
                        }
                    }

                    mmfw[offset] = null;
                    clusterOffsets.Remove(offset);
                }


                Console.WriteLine(string.Format("Count: {0}, AllocatedSize: {1}",i,mmfw.GetHeader().AllocatedSize));
            }
        }*/

        private static void Marshal<T>(T obj, byte[] buffer, int bufferOffset) {
            try {
                int size = SystemMarshal.SizeOf(typeof(T));
                IntPtr ptr = SystemMarshal.AllocHGlobal(size);
                SystemMarshal.StructureToPtr(obj, ptr, true);
                SystemMarshal.Copy(ptr, buffer, bufferOffset, size);
                SystemMarshal.FreeHGlobal(ptr);
            } catch(Exception ex) {
                throw ex;
            }
        }

        private static T Unmarshal<T>(byte[] buffer, int bufferOffset) where T : new() {
            T obj = new T();
            try {
                int size = SystemMarshal.SizeOf(typeof(T));
                IntPtr ptr = SystemMarshal.AllocHGlobal(size);
                SystemMarshal.Copy(buffer, bufferOffset, ptr, size);
                obj = (T)SystemMarshal.PtrToStructure(ptr, typeof(T));
                SystemMarshal.FreeHGlobal(ptr);
            } catch(Exception ex) {
                throw ex;
            }
            return obj;
        }

        private static void test4() {

            byte[] buffer = new byte[64];
            MyClass obj = new MyClass(rand.Next(), rand.NextDouble());
            Marshal<MyClass>(obj, buffer, 0);

        }

        #region To test PriorityQueue
        public class MyPriorityQueueItem {

            //public int Index { get; set; }

            public double Value { get; set; }

            public MyPriorityQueueItem(double value) {
                this.Value = value;
            }


            public bool Equals(MyPriorityQueueItem other) {
                return base.Equals(other);
            }

            public int Token { get; set; }
        }

        public class PriorityQueueContext : PriorityQueue<MyPriorityQueueItem>.IContext {

            #region IContext Members

            public int GetToken(MyPriorityQueueItem item) {
                return item.Token;
            }

            public void SetToken(MyPriorityQueueItem item, int token) {
                item.Token = token;
            }

            #endregion

            #region IComparer<MyPriorityQueueItem> Members

            public int Compare(MyPriorityQueueItem x, MyPriorityQueueItem y) {
                return x.Value.CompareTo(y.Value);
            }

            #endregion
        }

        private static double TestPriorityQueue(IEnumerable<MyPriorityQueueItem> items) {
            PriorityQueueContext context = new PriorityQueueContext();
            PriorityQueue<MyPriorityQueueItem> queue = PriorityQueue<MyPriorityQueueItem>.Create(context);

            DateTime beginTime = DateTime.Now;

            foreach(MyPriorityQueueItem item in items) {
                queue.Enqueue(item);
            }

            for(MyPriorityQueueItem topItem; queue.Dequeue(out topItem); ) {
                MyPriorityQueueItem nextTopItem;
                if(queue.Peek(out nextTopItem) && context.Compare(nextTopItem, topItem) > 0) {
                    Debug.Assert(false);
                }
                //queue.Enqueue(new MyPriorityQueueItem(topItem.Value + rand.NextDouble()));
                Debug.Print(topItem.Value.ToString());
                //Debug.Print(topItem.Value.ToString());
            }

            return (DateTime.Now - beginTime).TotalMilliseconds;
        }

        private static void test5() {

            List<MyPriorityQueueItem> items = new List<MyPriorityQueueItem>();
            for(int i = 0; i < 50; i++) {
                items.Add(new MyPriorityQueueItem(rand.NextDouble()));
            }

            Debug.Print(TestPriorityQueue(items).ToString());
            Console.ReadLine();
        }
        #endregion


        #region Test6
        //public class MyNeuralNetwork : SpikingNeuralNetwork.NeuralNetwork {

        //    public MyNeuralNetwork()
        //        : base(3, Directory.CreateDirectory(@"C:\Documents and Settings\Haymann\My Documents\Temp")) {
        //        for(long i = 0; i < this.NeuronCount; i++) {
        //            SpikingNeuralNetwork.Neuron preSynapseNeuron = this.GetNeuron(i);
        //            for(long j = 0; j < this.NeuronCount / 2; j++) {
        //                SpikingNeuralNetwork.Neuron postSynapseNeuron = this.GetNeuron(rand.Next((int)this.NeuronCount));
        //                SpikingNeuralNetwork.Synapse synapse = this.CreateSynapse(preSynapseNeuron, postSynapseNeuron);
        //                synapse.Weight = rand.NextDouble();
        //                synapse.Delay = rand.NextDouble();
        //            }
        //        }
        //    }
        //}

        private static void test6() {

            //DiscreteEvent<SpikingNeuralNetwork.NeuralState, SpikingNeuralNetwork.NeuralNetwork>.Runtime runtime = new DiscreteEvent<SpikingNeuralNetwork.NeuralState, SpikingNeuralNetwork.NeuralNetwork>.Runtime(new SpikingNeuralNetwork.NeuralNetwork(3, Directory.CreateDirectory(@"C:\Documents and Settings\Haymann\My Documents\Temp")), 1);

            //DateTime startTime = DateTime.Now;
            //int stepCount = 0;

            //for(int i = 0; i < 10; i++) {
            //    foreach(SpikingNeuralNetwork.Neuron neuron in runtime.Context.States) {
            //        runtime.ScheduleEvent(new SpikingNeuralNetwork.NeuralSpike(neuron), rand.NextDouble());
            //    }
            //}

            //while(runtime.Step()) {
            //    stepCount++;
            //    Debug.Print(string.Format("Step count: {0}, remaining event: {1}", stepCount, runtime.PendingEvents.Count));
            //}

            //System.Console.WriteLine("TotalMilliseconds/StepCount={0}", (DateTime.Now - startTime).TotalMilliseconds / stepCount);
            //System.Console.WriteLine("StepCount/TotalMilliseconds={0}", stepCount / (DateTime.Now - startTime).TotalMilliseconds);

            //Console.ReadLine();
        }
        #endregion

        #region Test8

        #endregion

        #region Test9
        /*
        public class Test9_MyClass : InstanceListService<Test9_MyClass, MyStruct>.Token, InstanceListService<Test9_MyClass, MyStruct>.IManagedObject {

            //private InstanceService<long, MyClass2, int>.Token _token;
            private MyStruct _data;
            private long _key;
            private InstanceService<long, Test9_MyClass, int> _service;

            public MyStruct Data {
                get { return this._data; }
            }

            InstanceService<long, Test9_MyClass, int>.Token InstanceService<long, Test9_MyClass, int>.IManagedObject.Token {
                get { 
                    //return this._token; 
                    return this;
                }
            }

            long InstanceService<long, Test9_MyClass, int>.IManagedObject.Key {
                get { return this._key; }
            }

            InstanceService<long, Test9_MyClass, int> InstanceService<long, Test9_MyClass, int>.IManagedObject.GetService() {
                return this._service;
            }

            public Test9_MyClass(long key, MyStruct data, InstanceService<long, Test9_MyClass, int> service) {
                //this._token = new InstanceService<long, MyClass2, int>.Token();
                this._key = key;
                this._data = data;
                this._service = service;
            }

            public int IntValue {
                get {
                    return this._data.@int;
                }
                set {
                    this._data.@int = value;
                }
            }

            public double DoubleValue {
                get {
                    return this._data.@double;
                }
                set {
                    this._data.@double = value;
                }
            }
        }

        private static void test9() {

            int size = 1024 * 1024;
            InstanceListService<Test9_MyClass, MyStruct> service = null;
            service = new InstanceListService<Test9_MyClass, MyStruct>(size, @"C:\Documents and Settings\Haymann\My Documents\Temp\mmfArr_test.dat", "mmfArr_test",
                (key, data) => {
                    return new Test9_MyClass(key, data, service);
                });

            Dictionary<long, MyStruct> dict = new Dictionary<long, MyStruct>();

            for(int i = 0; i < int.MaxValue; i++) {
                long key = rand.Next(size);
                if(dict.ContainsKey(key)) {
                    MyStruct strt1 = service[key].Data;
                    MyStruct strt2 = dict[key];
                    if(!strt1.Equals(strt2)) {
                        Debug.Assert(false);
                    }
                    dict.Remove(key);
                } else {
                    service[key].IntValue = rand.Next();
                    service[key].DoubleValue = rand.NextDouble();
                    dict[key] = service[key].Data;
                }
                Console.WriteLine(i);
            }

            Console.ReadLine();
        }
        */
        #endregion
    }

}
